/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "glheaders.h"
#include "renderUtils.h"

#include <math.h>
#include <string.h>
#include <stdlib.h>

#include <fstream>
#include <iostream>

#include <QGLWidget>
#include <QFont>
#include <QPainter>

extern QGLWidget *widget;

void arc(float radius, float theta0, float theta1, bool line )  {
  float dtheta = 2*M_PI/50.0;
  if(line) glBegin(GL_LINE_LOOP);
  else glBegin(GL_TRIANGLE_FAN);
  glVertex3f(0,0,0);
  for(float theta=theta0;theta<=theta1;theta+=dtheta) {
    float xc =  radius*cos(theta);
    float yc =  radius*sin(theta);
    glVertex3f(xc,yc,0);
  }
  float xc =  radius*cos(theta1);
  float yc =  radius*sin(theta1);
  glVertex3f(xc,yc,0);
  glEnd();
}

void arc2(float radius1, float radius2, float theta0, float theta1, bool line )  {
  float dtheta = 2*M_PI/50.0;
  if(line) {
    glBegin(GL_LINE_LOOP);
    for(float theta=theta0;theta<=theta1;theta+=dtheta) {
      float xc =  radius1*cos(theta);
      float yc =  radius1*sin(theta);
      glVertex3f(xc,yc,0);
    }
    float xc =  radius1*cos(theta1);
    float yc =  radius1*sin(theta1);
    glVertex3f(xc,yc,0);
    for(float theta=theta1;theta>=theta0;theta-=dtheta) {
      float xc =  radius2*cos(theta);
      float yc =  radius2*sin(theta);
      glVertex3f(xc,yc,0);
    }
    xc =  radius2*cos(theta0);
    yc =  radius2*sin(theta0);
    glVertex3f(xc,yc,0);
    glEnd();
  }
  else {
    glBegin(GL_TRIANGLE_STRIP);
    for(float theta=theta0;theta<=theta1;theta+=dtheta) {
      float xc =  radius1*cos(theta);
      float yc =  radius1*sin(theta);
      glVertex3f(xc,yc,0);
      
      xc =  radius2*cos(theta);
      yc =  radius2*sin(theta);
      glVertex3f(xc,yc,0);
    }
    float xc =  radius1*cos(theta1);
    float yc =  radius1*sin(theta1);
    glVertex3f(xc,yc,0);
    xc =  radius2*cos(theta1);
    yc =  radius2*sin(theta1);
    glVertex3f(xc,yc,0);

    glEnd();
  }
}

void semiSuperQuadric(float radius, float s, float t, float a, float b, bool line) {
  if(line) glBegin(GL_LINE_LOOP);
  else glBegin(GL_TRIANGLE_FAN);
  //glColor4f(0.8, 0.8, 0.8, 0.7);
  if(!line) glVertex3f(0,0,0);
  float dtheta = 2*M_PI/50.0;
  for(float theta=0;theta<=M_PI;theta+=dtheta) {
    float xc =  radius*cos(theta);
    float yc =  radius*sin(theta);

    if(theta+dtheta>M_PI) {
      xc = -1.0*radius;
      yc = 0;
    }

    float sgnx = (xc<0)? -1.0: 1.0;
    float sgny = (yc<0)? -1.0: 1.0;
    xc = sgnx*pow(fabs(xc), s)*a;
    yc = sgny*pow(fabs(yc), t)*b;
    //printf("%f %f\n", xc, yc);
    //glColor4f(0.2*yc+0.8, 0.2*yc+0.8, 0.2*yc+0.8, 0.7);
    glVertex3f(xc,yc,0);
  } 
  float xc = radius;
  float sgnx = (xc<0)? -1.0: 1.0;
  xc = sgnx*pow(fabs(xc), s)*a;

  glColor4f(0.8,0.8,0.8,0.7);
  //glVertex3f(xc,0,0);
  glEnd();
}

void superQuadric(float radius, float s, float t, float a, float b, bool line) {
  if(line) glBegin(GL_LINE_LOOP);
  else glBegin(GL_TRIANGLE_FAN);
  glColor4f(0.8, 0.8, 0.8, 0.7);
  if(!line) glVertex3f(0,0,0);
  float dtheta = 2*M_PI/50.0;
  for(float theta=0;theta<=2*M_PI;theta+=dtheta) {
    float xc =  radius*cos(theta);
    float yc =  radius*sin(theta);
    float sgnx = (xc<0)? -1.0: 1.0;
    float sgny = (yc<0)? -1.0: 1.0;
    xc = sgnx*pow(fabs(xc), s)*a;
    yc = sgny*pow(fabs(yc), t)*b;
    //printf("%f %f\n", xc, yc);
    glColor4f(0.2*yc+0.8, 0.2*yc+0.8, 0.2*yc+0.8, 0.7);
    glVertex3f(xc,yc,0);
  } 
  float xc = radius;
  float sgnx = (xc<0)? -1.0: 1.0;
  xc = sgnx*pow(fabs(xc), s)*a;

  glColor4f(0.8,0.8,0.8,0.7);
  glVertex3f(xc,0,0);
  glEnd();
}

void roundRectangleLine(float x, float y, float w, float h, float roundnessx, float roundnessy) {
  int res = 5.0; // resolution of rounded part
	float r1 = roundnessx;
	float r2 = roundnessy<0? roundnessx:roundnessy;
  glBegin(GL_LINE_LOOP);
  // North East
  for(int i=0;i<=(int) res;i++) {
    float angle = (float) i/res*M_PI/2.0;
    float dx = r1*cos(angle);
    float dy = r2*sin(angle);
    float x1 = x + w + dx;
    float y1 = y + h + dy;
      glVertex2f(x1,y1);
  }
  // North West
  for(int i=0;i<=(int) res;i++) {
    float angle = (float) i/res*M_PI/2.0 + M_PI/2.0;
    float dx = r1*cos(angle);
    float dy = r2*sin(angle);
    float x1 = x + 0 + dx;
    float y1 = y + h + dy;
    glVertex2f(x1,y1);
  }
  
  // South West
  for(int i=0;i<=(int) res;i++) {
    float angle = (float) i/res*M_PI/2.0 + M_PI;
    float dx = r1*cos(angle);
    float dy = r2*sin(angle);
    float x1 = x + 0 + dx;
    float y1 = y + dy;
    glVertex2f(x1,y1);
  }
  
  // South East
  for(int i=0;i<=(int) res;i++) {
    float angle = (float) i/res*M_PI/2.0+ 3.0*M_PI/2.0;
    float dx = r1*cos(angle);
    float dy = r2*sin(angle);
    float x1 = x + w + dx;
    float y1 = y + dy;
    glVertex2f(x1,y1);
  }
  
  glEnd();
}
void roundRectangle(float x, float y, float w, float h, float roundnessx, float roundnessy) {
  int res = 5.0; // resolution of rounded part
  float r1 = roundnessx;
  float r2 = roundnessy<0? roundnessx:roundnessy;
 
  glBegin(GL_TRIANGLE_FAN);
    glVertex2f(x+0.5*w, y+0.5*h);
    // North East
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + w + dx;
      float y1 = y + h + dy;
      glVertex2f(x1,y1);
    }
    // North West
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0 + M_PI/2.0;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + 0 + dx;
      float y1 = y + h + dy;
      glVertex2f(x1,y1);
    }

    // South West
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0 + M_PI;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + 0 + dx;
      float y1 = y + dy;
      glVertex2f(x1,y1);
    }

    // South East
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0+ 3.0*M_PI/2.0;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + w + dx;
      float y1 = y + dy;
      glVertex2f(x1,y1);
    }
    glVertex2f(x+w+r1, y+h);

    glEnd();

}

void shadowRectangle(float x, float y, float w, float h, float roundnessx, float roundnessy, float alpha, float /*offset*/) {
  int res = 5.0; // resolution of rounded part
  float r;
  float r1 = roundnessx;
  float r2 = roundnessy;
    glBegin(GL_QUADS);
    {
      // North
      r = r2;
      glColor4f(0,0,0,alpha);
      glVertex2f(x+0, y + h);
      glVertex2f(x+w, y + h);
      glColor4f(0,0,0,0);
      glVertex2f(x+w, y + h + r2);
      glVertex2f(x+0, y + h + r2);
      // South
      r = r1;
      glColor4f(0,0,0,alpha);
      glVertex2f(x+0, y );
      glVertex2f(x+w, y );
      glColor4f(0,0,0,0);
      glVertex2f(x+w, y  - r2);
      glVertex2f(x+0, y  - r2);
      // East
      r = r1;
      glColor4f(0,0,0,alpha);
      glVertex2f(x+w, y );
      glVertex2f(x+w, y + h);
      glColor4f(0,0,0,0);
      glVertex2f(x+w+r1, y + h);
      glVertex2f(x+w+r1, y );
      // West
      r = r2;
      glColor4f(0,0,0,alpha);
      glVertex2f(x, y );
      glVertex2f(x, y + h);
      glColor4f(0,0,0,0);
      glVertex2f(x-r1, y + h);
      glVertex2f(x-r1, y );
    }
    glEnd();

    glBegin(GL_TRIANGLE_FAN);
    glColor4f(0,0,0,alpha);
    glVertex2f(x+w, y + h);
    // North East
    glColor4f(0,0,0,0.0);
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + w + dx;
      float y1 = y + h + 0 + dy;
      glVertex2f(x1,y1);
    }
    glEnd();

    // North West
    glBegin(GL_TRIANGLE_FAN);
    glColor4f(0,0,0,alpha);
    glVertex2f(x, y + h);
    glColor4f(0,0,0,0.0);
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0 + M_PI/2.0;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + 0 + dx;
      float y1 = y + h + 0 + dy;
      glVertex2f(x1,y1);
    }
    glEnd();

    // South West
    glBegin(GL_TRIANGLE_FAN);
    glColor4f(0,0,0,alpha);
    glVertex2f(x, y );
    glColor4f(0,0,0,0.0);
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0 + M_PI;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + 0 + dx;
      float y1 = y  + dy;
      glVertex2f(x1,y1);
    }
    glEnd();

    // South East
    glBegin(GL_TRIANGLE_FAN);
    glColor4f(0,0,0,alpha);
    glVertex2f(x+w, y );
    glColor4f(0,0,0,0.0);
    for(int i=0;i<=(int) res;i++) {
      float angle = (float) i/res*M_PI/2.0+ 3.0*M_PI/2.0;
      float dx = r1*cos(angle);
      float dy = r2*sin(angle);
      float x1 = x + w + dx;
      float y1 = y  + dy;
      glVertex2f(x1,y1);
    }

    glEnd();
}


void rectangleAndShadow(float x, float y, float w, float h, float roundness, float alpha) {
  glRectf(x,y,x+w, y+h);
  shadowRectangle(x,y,w,h, roundness, alpha);
  glLineWidth(1);
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  glColor4f(1,1,1,alpha);
  glRectf(x,y,x+w, y+h);
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

void circle(float radius, bool line) {
  if(line) glBegin(GL_LINE_LOOP);
  else glBegin(GL_TRIANGLE_FAN);
  if(!line) glVertex2f(0,0);
  float dtheta = 2*M_PI/25.0;
  for(float theta=0;theta<=2*M_PI;theta+=dtheta) {
    float xc =  radius*cos(theta);
    float yc =  radius*sin(theta);
    //float r = sin(theta);
    //glColor4f(r*0.5+0.5,0.1*(r*0.5+0.5),0,1.0);
    glVertex2f(xc,yc);
  } 
  glVertex2f(radius,0);
  glEnd();
}

void fuzzyCircle(float radius1, float alpha1, float radius2, float alpha2, float r, float g, float b) {
  glBegin(GL_TRIANGLE_STRIP);
  float dtheta = 2*M_PI/25.0;
  for(float theta=0;theta<2*M_PI;theta+=dtheta) {
    float x1 =  radius1*cos(theta);
    float y1 =  radius1*sin(theta);

    float x2 =  radius2*cos(theta);
    float y2 =  radius2*sin(theta);
    //float r = sin(theta);
    glColor4f(r, g, b, alpha1);
    glVertex2f(x1,y1);

    glColor4f(r, g, b, alpha2);
    glVertex2f(x2,y2);

  } 
  glColor4f(r, g, b, alpha1);
  glVertex2f(radius1,0);
  glColor4f(r, g, b, alpha2);
  glVertex2f(radius2,0);
  glEnd();
}

void elongatedCircle(float w, float h, bool line) {
  float radius = 0.5*h;
  float radius1 = 0.5*w;
  float radius2 = 0.5*h;
  float x0 = 0.5*w - 0.5*h;
  
  if(line) glBegin(GL_LINE_LOOP);
  else glBegin(GL_TRIANGLE_FAN);
  if(!line) glVertex3f(x0,0,0);
  float dtheta = 2*M_PI/50.0;
  for(float theta=0;theta<=2*M_PI;theta+=dtheta) {
    float x0;
    if(theta>M_PI/2.0 && theta<3.0*M_PI/2.0) {
      x0 = -(radius1-radius2);
    } else {
      x0 = radius1-radius2;
    }
    float xc = x0 +  radius*cos(theta);
    float yc =  radius*sin(theta);
    //float r = sin(theta);
    //glColor4f(r*0.5+0.5,0.1*(r*0.5+0.5),0,1.0);
    glVertex3f(xc,yc,0);
  } 
  glVertex3f(x0 + radius,0,0);
  glEnd();
}

void circle(float x, float y, float radius, bool line) {
  if(line) glBegin(GL_LINE_LOOP);
  else glBegin(GL_TRIANGLE_FAN);
  if(!line) glVertex3f(x,y,0);
  float dtheta = 2*M_PI/50.0;
  for(float theta=0;theta<=2*M_PI;theta+=dtheta) {
    float xc =  x + radius*cos(theta);
    float yc =  y + radius*sin(theta);
    //float r = sin(theta);
    //glColor4f(r*0.5+0.5,0.1*(r*0.5+0.5),0,1.0);
    glVertex3f(xc,yc,0);
  } 
  glVertex3f(x + radius,y,0);
  glEnd();
}



void donut(float radius1, float radius2, float a1, float a2, float r, float g, float b, bool /*line*/, float angle0, float angle1) {
  glBegin(GL_TRIANGLE_STRIP);
  float dtheta = 2*M_PI/50.0;
  for(float theta=angle0;theta<=angle1;theta+=dtheta) {
    float xc =  radius1*cos(theta);
    float yc =  radius1*sin(theta);
    glColor4f(r,g,b,a1);
    glVertex3f(xc,yc,0);

    xc =  radius2*cos(theta);
    yc =  radius2*sin(theta);
    glColor4f(r,g,b,a2);
    glVertex3f(xc,yc,0);
  } 
  float xc =  radius1*cos(angle1);
  float yc =  radius1*sin(angle1);
  glColor4f(r,g,b,a1);
  glVertex3f(xc,yc,0);
  xc =  radius2*cos(angle1);
  yc =  radius2*sin(angle1);
  glColor4f(r,g,b,a2);
  glVertex3f(xc,yc,0);
  glEnd();
}

void arc(float radius, float angle0, float angle1) {
	glBegin(GL_LINE_STRIP);
	float dtheta = 2*M_PI/50.0;
	for(float theta=angle0;theta<=angle1;theta+=dtheta) {
		float xc =  radius*cos(theta);
		float yc =  radius*sin(theta);
		glVertex3f(xc,yc,0);
	} 
	float xc =  radius*cos(angle1);
	float yc =  radius*sin(angle1);
	glVertex3f(xc,yc,0);
	glEnd();
}


void drawRotatedText(float /*x*/, float /*y*/, float /*angle*/, const char */*str*/, const char */*font*/, int /*fontsize*/) {
  //To be done. Any ideas?
}
void drawText(float x, float y, const char *str, const char *font, int fontsize, string style) {
	QFont sans(font, fontsize);
	if(style == "bold") {
		sans.setWeight(QFont::Bold);
	} else {
		sans.setWeight(QFont::Normal);
	}

  if(widget) widget->renderText(x,y, 0, (str), sans);
}

int currentFontSize = 10;
char currentFont[512]; 
void setFont(char *font, int size) {
	currentFontSize = size;
	strncpy(currentFont, font, 512);
}
void drawText(float x, float y, float /*z*/, const char *msg, ...) {
    va_list args;
    va_start(args, msg);
	

	char str[512];
    vsnprintf(str, 512,msg, args);
	
	drawText(x,y, str, currentFont, currentFontSize);
    va_end(args);
}

void drawTextBold(float x, float y, const char *str, const char *font, int fontsize) {
  QFont sansBold(font, fontsize);
  sansBold.setWeight(QFont::Bold);
	if(widget) {
		widget->makeCurrent();
		widget->renderText(x,y, 0.0, (str), sansBold);
	}
}
void getBoundsText(int &x, int &y, int &w, int &h, const char *str, const char *font, int fontsize, string /*style*/) {
  QFont sans(font, fontsize);
  QRect r = QFontMetrics(sans).boundingRect(str);
  x = r.x();
  y = r.y();
  w = r.width();
  h = r.height();
}

/* The SAME_SIGNS macro assumes arithmetic where the exclusive-or    */
/* operation will work on sign bits.  This works for twos-complement,*/
/* and most other machine arithmetic.                                */
#define SAME_SIGNS( a, b ) \
	(((long) ((unsigned long) a ^ (unsigned long) b)) >= 0 )

// [Danni] Converted to REAL. The line intersection algorithm determines if
//         two lines collide by drawing a boundingbox around the two lines
//         and identify if any points are within another's box.

//         This is a constant time function, hence we can quickly test if 
//         a trace will hit a player in 2D space by stating players bbox 
//         widths are one line and the clients view angle is another.

//         Forewarning! Bounding boxes do not always fully engulf the model.
//         My personal preference is to make the bbox 3 times it's normal size.

bool lines_intersect(float x1,float y1,float x2,float y2,float x3,float y3,
			float x4,float y4) {

	float Ax,Bx,Cx,Ay,By,Cy,d,e,f,num,offset;
	float x1lo,x1hi,y1lo,y1hi;

	Ax = x2-x1;
	Bx = x3-x4;

	if(Ax<0) {					/* X bound box test*/
		x1lo=(float)x2; x1hi=(float)x1;
	} else {
		x1hi=(float)x2; x1lo=(float)x1;
	}
	
	if(Bx>0) {
		if(x1hi < (float)x4 || (float)x3 < x1lo) return false;
	} else {
		if(x1hi < (float)x3 || (float)x4 < x1lo) return false;
	}
	Ay = y2-y1;
	By = y3-y4;

	if(Ay<0) {					/* Y bound box test*/
		y1lo=(float)y2; y1hi=(float)y1;
	} else {
		y1hi=(float)y2; y1lo=(float)y1;
	}

	if(By>0) {
		if(y1hi < (float)y4 || (float)y3 < y1lo) return false;
	} else {
		if(y1hi < (float)y3 || (float)y4 < y1lo) return false;
	}
	Cx = x1-x3;
	Cy = y1-y3;
	d = By*Cx - Bx*Cy;					/* alpha numerator*/
	f = Ay*Bx - Ax*By;					/* both denominator*/
	if(f>0) {						/* alpha tests*/
	  if(d<0 || d>f) return false;
	} else {
	  if(d>0 || d<f) return false;
	}
	e = Ax*Cy - Ay*Cx;					/* beta numerator*/
	if(f>0) {						/* beta tests*/
	  if(e<0 || e>f) return false;
	} else {
	  if(e>0 || e<f) return false;
	}
	
	/*compute intersection coordinates*/
	
	if(f==0) return false;
	num = d*Ax;						/* numerator */
	offset = SAME_SIGNS(num,f) ? f/2 : -f/2;		/* round direction*/
	
	num = d*Ay;
	offset = SAME_SIGNS(num,f) ? f/2 : -f/2;
	
	return true;
}
